package br.ufrgs.ga;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import br.ufrgs.ga.fitness.TspTourFitnessCalculator;
import br.ufrgs.ga.model.GeneticModelFactory;
import br.ufrgs.ga.model.Individual;
import br.ufrgs.ga.model.Population;
import br.ufrgs.tsp.model.ICity;

/**
 * Algorithm main class. All algorithm logic is encapsulated here.
 * @author mineiro
 *
 */
public class TSPGeneticAlgorithm extends GeneticAlgorithm {

	/**
	 * Initial configuration of the cities in a TSP instance.
	 */
	private List<ICity> citiesConfiguration;
	
	
	public TSPGeneticAlgorithm(List<ICity> citiesConfiguration) {
		this.citiesConfiguration = citiesConfiguration;
		this.fitnessCalculator = new TspTourFitnessCalculator(citiesConfiguration);
	}
	
	/**
	 * Creates a random {@link Individual} instance based on the <code>citiesConfiguration</code> vector.
	 * 
	 * @return An Individual instance randomly created based on the cities configuration vector.
	 */
	private Individual createRandomIndividual() {
		Random rand = new Random();
		GeneticModelFactory factory = GeneticModelFactory.getInstance();
		
		List<ICity> cities = new LinkedList<ICity>();
		for (ICity city : citiesConfiguration)
			cities.add(city);
		
		String dnaStrand = "";
		for(int i = 0; i < citiesConfiguration.size(); i++) {
			int index = rand.nextInt(cities.size());
			dnaStrand += cities.get(index).getCityId();
			cities.remove(cities.get(index));
		}
		dnaStrand += dnaStrand.substring(0, 1);
		
		return new Individual(factory.createChromosome(dnaStrand), fitnessCalculator);
	}	
	
	@Override
	public void initializePopulation() {
		this.population = new Population();
		
		for(int i = 0; i < Population.POPULATION_SIZE; i++) {
			Individual individual = createRandomIndividual();
			population.add(individual);
		}
		
		this.bestIndividualSoFar = population.getMostAdaptedIndividual();
	}
	
	@Override
	public boolean solutionFound() {
		if (this.bestIndividualSoFar.getFitnessValue().equals(TspTourFitnessCalculator.TSP_BEST_FITNESS_VALUE)) {
			return true;
		}
		return false;
	}
}
